package com.lc.communication.handle;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import com.lc.communication.constants.ChannelConstant;
import com.lc.communication.util.ChannelMap;
import com.lc.communication.vo.MessageProtocol;
import com.lc.communication.vo.RequestVo;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentHashMap.KeySetView;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liuhf17
 * @version 1.0.0
 * @ClassName HeartBeatClientHandler.java
 * @Description TODO
 * @createTime 2024年06月11日 14:14:00
 */
public class HeartBeatServerHandler extends SimpleChannelInboundHandler<MessageProtocol> {
    private static final Logger log = LoggerFactory.getLogger(HeartBeatServerHandler.class);

    public HeartBeatServerHandler() {
        channelHeart();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("服务端 channelInactive");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception {
        log.info(" ====== > [server] message received : {}", JSONUtil.toJsonStr(new String(msg.getContent())));
        RequestVo vo = null;
        try {
            vo = JSONUtil.toBean(new String(msg.getContent()), RequestVo.class);
        } catch (Exception e) {
            log.error("error 结构错误：{}", msg);
        }
        //客户端连接建立，记录 客户端能收到消息的channel
        if (ChannelConstant.CLIENT_LINK_SUCCESS.equals(vo.getType())) {
            ChannelMap.CHANNEL_CLIENT_MAP.put(vo.getClientUuid(), ctx.channel());
            ChannelMap.CLIENT_NAME_UUID.put(vo.getClientName(), vo.getClientUuid());
            //ctx.channel().writeAndFlush(vo.toRequestVo());
        }
        //心跳通信
        else if (ChannelConstant.TYPE_HEART.equals(vo.getType())) {
            vo.setResBody("server dong dong dong...");
            ctx.channel().writeAndFlush(vo.toRequestVo());
            //log.info("服务端收到客户端的心跳：{}", JSONUtil.toJsonStr(vo));
            refreshChannelMap(vo, ctx.channel());
        } else if (ChannelConstant.CLIENT_REPLY.equals(vo.getType())) {
            log.info("客户端正常通信回复消息:{}", JSONUtil.toJsonStr(vo));
        } else if (ChannelConstant.REQUEST_MSG.equals(vo.getType())) {
            //获取本次消息唯一标识
            if (ChannelMap.resultMap.containsKey(vo.getId())) {
                ChannelMap.resultMap.put(vo.getId(), JSONUtil.toJsonStr(vo));
            } else {
                vo.setError("服务端未获取到请求标识：" + vo.getId());
                //ctx.channel().writeAndFlush(vo.toRequestVo());
                log.error("服务端未获取到请求标识：{}", vo.getId());
            }
        } else {
            vo.setBody("服务端处理好了！！！！");
            log.info("HeartBeatServerHandler->其他信息处理：{}", JSONUtil.toJsonStr(vo));
            ctx.channel().writeAndFlush(vo.toRequestVo());
        }

    }

    /*@Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;

        String eventType = null;
        switch (event.state()) {
            case READER_IDLE:
                eventType = "读空闲";
                readIdleTimes++; // 读空闲的计数加1
                break;
            case WRITER_IDLE:
                eventType = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                eventType = "读写空闲";
                // 不处理
                break;
        }
        System.out.println(ctx.channel().remoteAddress() + "超时事件：" + eventType);
        if (readIdleTimes > 3) {
            System.out.println(" [server]读空闲超过3次，关闭连接，释放更多资源");
            ctx.channel().writeAndFlush("idle close");
            //ChannelMap.CHANNEL_MAP.remove(ChannelMap.SERVER_A);
            ctx.channel().close();
        }
    }*/

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("=== " + ctx.channel().remoteAddress() + " is active ===");
    }

    public void refreshChannelMap(RequestVo vo, Channel channel) {
        ChannelMap.CHANNEL_CLIENT_MAP.put(vo.getClientUuid(), channel);
    }

    /**
     * 检测客户端活性
     */
    public void channelHeart() {
        new Thread(() -> {
            while (true) {
                ThreadUtil.sleep(ChannelConstant.SERVER_HEART_TIME);
                KeySetView<String, Channel> strings = ChannelMap.CHANNEL_CLIENT_MAP.keySet();
                for (String key : strings) {
                    Channel channel = ChannelMap.CHANNEL_CLIENT_MAP.get(key);
                    if (null == channel) {
                        ChannelMap.removeChannelClientMap(key);
                    } else if (!channel.isActive()) {
                        ConcurrentHashMap<String, AtomicInteger> channelClientMapCount = ChannelMap.CHANNEL_CLIENT_MAP_COUNT;
                        if (!channelClientMapCount.containsKey(key)) {
                            channelClientMapCount.put(key, new AtomicInteger(1));
                            log.info("【server】 client 【{}】【{}】 maybe alive 【count：{}】", key, ChannelMap.getKeyByValue(key), 1);
                            continue;
                        }
                        AtomicInteger count = channelClientMapCount.get(key);
                        if (count.get() > ChannelConstant.SERVER_HEART_COUNT) {
                            ChannelMap.removeChannelClientMap(key);
                            log.info("【server】 client 【{}】【{}】 die", key, ChannelMap.getKeyByValue(key));
                        } else {
                            int i = count.addAndGet(1);
                            log.info("【server】 client 【{}】【{}】 maybe alive 【count：{}}", key, ChannelMap.getKeyByValue(key), i);
                        }
                    } else {
                        log.info("【server】 client 【{}】【{}】 live", key, ChannelMap.getKeyByValue(key));
                    }
                }

            }
        }).start();
    }
}
